<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第14章 - ジェネレーター</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="13-I18n-and-L10n.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="15-Unit-and-Functional-Testing.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.14.admin.generator" id="chapter.14.admin.generator"></a><h1>第14章 - ジェネレーター</h1>

<p>多くのアプリケーションはデータベースに保存されたデータに基づいており、それにアクセスするためのインターフェイスを提供します。symfonyはPropelオブジェクトに基づいたデータ操作機能を提供するモジュール作成の反復タスクを自動化します。オブジェクトモデルが適切に定義したのであれば、symfonyはサイト全体の管理機能(administration)も自動的に生成します。この章では、symfonyに搭載された2つのジェネレーター: scaffoldingジェネレーターとadministrationジェネレーターをお教えします。後者は完全な構文による特別な設定ファイルに依存するので、この章の多くはadministrationジェネレーターのさまざまな可能性について説明します。</p>

<div class="toc">
<dl>
<dt><a href="#code.generation.based.on.the.model">14.1. モデルをもとにコードを生成する</a></dt>
<dd><dl>
<dt><a href="#example.data.model">14.1.1. データモデルの例</a></dt>
</dl></dd>
<dt><a href="#administration">14.2. administration</a></dt>
<dd><dl>
<dt><a href="#initiating.an.administration.module">14.2.1. administrationモジュールを初期化する</a></dt>
<dt><a href="#a.look.at.the.generated.code">14.2.2. 生成コードを見る</a></dt>
<dt><a href="#introducing.the.generator.yml.configuration.file">14.2.3. generator.yml設定ファイルを導入する</a></dt>
</dl></dd>
<dt><a href="#generator.configuration">14.3. ジェネレーターの設定</a></dt>
<dd><dl>
<dt><a href="#fields">14.3.1. フィールド</a></dt>
<dd><dl>
<dt><a href="#field.settings">14.3.1.1. フィールドの設定</a></dt>
<dt><a href="#adding.fields.to.the.display">14.3.1.2. display設定にフィールドを追加する</a></dt>
<dt><a href="#custom.fields">14.3.1.3. カスタムフィールド</a></dt>
<dt><a href="#partial.fields">14.3.1.4. 部分テンプレートフィールド</a></dt>
</dl></dd>
<dt><a href="#view.customization">14.3.2. ビューのカスタマイゼーション</a></dt>
<dd><dl>
<dt><a href="#changing.the.view.title">14.3.2.1. ビューのタイトルを変更する</a></dt>
<dt><a href="#adding.tooltips">14.3.2.2. ツールチップを追加する</a></dt>
<dt><a href="#modifying.the.date.format">14.3.2.3. 日付の書式を修正する</a></dt>
</dl></dd>
<dt><a href="#list.viewspecific.customization">14.3.3. listビュー固有のカスタマイゼーション</a></dt>
<dd><dl>
<dt><a href="#changing.the.layout">14.3.3.1. レイアウトを変更する</a></dt>
<dt><a href="#filtering.the.results">14.3.3.2. 結果をフィルタリングする</a></dt>
<dt><a href="#sorting.the.list">14.3.3.3. リストをソートする</a></dt>
<dt><a href="#customizing.the.pagination">14.3.3.4. ページ分割をカスタマイズする</a></dt>
<dt><a href="#using.a.join.to.speed.up.page.delivery">14.3.3.5. ページ配信を加速するためにJoinを使う</a></dt>
</dl></dd>
<dt><a href="#new.and.edit.viewspecific.customization">14.3.4. newとeditビュー固有のカスタマイズ</a></dt>
<dd><dl>
<dt><a href="#handling.partial.fields">14.3.4.1. 部分テンプレートフィールドを扱う</a></dt>
</dl></dd>
<dt><a href="#dealing.with.foreign.keys">14.3.5. 外部キーを扱う</a></dt>
<dd><dl>
<dt><a href="#onetomany.relationships">14.3.5.1. 一対多のリレーション</a></dt>
<dt><a href="#manytomany.relationships">14.3.5.2. 多対多のリレーション</a></dt>
</dl></dd>
<dt><a href="#adding.interactions">14.3.6. インタラクションを追加する</a></dt>
<dt><a href="#form.validation">14.3.7. フォームのバリデーション</a></dt>
<dt><a href="#restricting.user.actions.using.credentials">14.3.8. クレデンシャルを利用してユーザーのアクションを制限する</a></dt>
</dl></dd>
<dt><a href="#modifying.the.presentation.of.generated.modules">14.4. 生成モジュールのプレゼンテーションを修正する</a></dt>
<dd><dl>
<dt><a href="#using.a.custom.style.sheet">14.4.1. カスタムスタイルシートを使う</a></dt>
<dt><a href="#creating.a.custom.header.and.footer">14.4.2. カスタムヘッダーとフッターを生成する</a></dt>
<dt><a href="#customizing.the.theme">14.4.3. テーマをカスタマイズする</a></dt>
</dl></dd>
<dt><a href="#summary">14.5. まとめ</a></dt>
</dl>
</div>
<a name="code.generation.based.on.the.model" id="code.generation.based.on.the.model"></a><h2>モデルをもとにコードを生成する</h2>

<p>Webアプリケーションにおいて、データアクセスオペレーションはつぎのように分類できます:</p>

<ul>
<li>レコードの作成</li>
<li>レコードの検索</li>
<li>レコードの更新(とカラムの修正)</li>
<li>レコードの削除</li>
</ul>

<p>これらのオペレーションは共通なので、頭文字をとった専用の略語であるCRUD(Create、Retrieval、Update、Deletion)が存在します。多くのページはこれらの1つに還元できます。たとえばフォーラムのアプリケーションにおいて、最新投稿のリストは検索オペレーション(retrieve)で、投稿への返答は作成オペレーション(create)に対応します。</p>

<p>任意のテーブルに対するCRUDオペレーションを実装する基本的なアクションとテンプレートはWebアプリケーション内部で繰り返し作られます。symfonyにおいて、バックエンドインターフェイスの初期開発を加速するために、モデルレイヤーはCRUDオペレーションを生成できるようにするための十分な情報を持ちます。</p>

<a name="example.data.model" id="example.data.model"></a><h3>データモデルの例</h3>

<p>この章全体を通して、一覧表示機能はシンプルな例に基づいたsymfonyのadminジェネレーターの機能を示します。これによって8章を思い出すでしょう。これは2つの<code>BlogArticle</code>クラスと<code>BlogComment</code>クラスを含む、blogアプリケーションのよく知られた例です。図14-1で描かれているように、リスト14-1はスキーマを示します。</p>

<p>リスト14-1 - blogログアプリケーションの<code>schema.yml</code>の例</p>

<pre class="yml">propel:
  blog_article:
    id:          ~
    title:       varchar(255)
    content:     longvarchar
    created_at:  ~
  blog_comment:
    id:               ~
    blog_article_id:  ~
    author:           varchar(255)
    content:          longvarchar
    created_at:       ~</pre>

<p>図14-1 データモデルの例</p>

<p><img src="images/F1401.png" alt="データモデルの例" title="データモデルの例" /></p>

<p>コード生成を可能にするためにスキーマ作成の期間で従わなければならない特別なルールは存在しません。symfonyはスキーマをそのまま使い、administrationを生成するためにスキーマの属性を解釈します。</p>

<blockquote class="tip"><p>
  この章を最大限に活用するには、例の内容を実際に行う必要があります。リストで説明されたすべてのステップを眺めるのであれば、symfonyが何を生成し、生成されたコードで何が行われるのかということをより理解できるようになります。以まえに説明されたようなデータ構造を作成したり、<code>blog_article</code>と<code>blog_comment</code>テーブルをともなうデータベースを作成したり、サンプルデータをこのデータベースに投入したくなるでしょう。</p>
</blockquote>

<a name="administration" id="administration"></a><h2>administration</h2>

<p>symfonyは、バックエンドアプリケーションのために、<code>schema.yml</code>ファイルからのモデルクラス定義に基づいて、モジュールを作成できます。生成されたadministrationモジュールだけを用いてサイト全体のadministrationを作成できます。このセクションの例において<code>backend</code>アプリケーションに追加されるadministraionモジュールを説明します。プロジェクトが<code>backend</code>アプリケーションを持たない場合、<code>generate:app</code>タスクを呼び出してスケルトンを作成します:</p>

<pre><code>&gt; php symfony generate:app backend
</code></pre>

<p>administrationモジュールは<code>generator.yml</code>という名前の特別な設定ファイルを通してモデルを解釈します。すべての生成コンポーネントとモジュールの外見を拡張するために<code>generator.yml</code>ファイルを変更できます。このようなモジュールは通常のモジュールメカニズムからの恩恵を受けます(レイアウト、ルーティング、カスタム設定、オートロードなど)。独自機能を生成されたadministrationに統合するために、生成されたアクションもしくはテンプレートを上書きすることもできますが、<code>generator.yml</code>はもっとも共通の要件を考慮して、PHPコードの使いかたを限定します。</p>

<blockquote class="note"><p>
  たいていの要件は<code>generator.yml</code>設定ファイルでカバーされますが、この章の後のほうで見るように、設定クラスを通してadministrationモジュールを設定することもできます。</p>
</blockquote>

<a name="initiating.an.administration.module" id="initiating.an.administration.module"></a><h3>administrationモジュールを初期化する</h3>

<p>モデルを基本単位としてsymfonyコマンドでadministrationをビルドします。モジュールは<code>propel:generate-admin</code>タスクを利用するPropelオブジェクトに基づいて生成されます:</p>

<pre><code>&gt; php symfony propel:generate-admin backend BlogArticle --module=article
</code></pre>

<p>この呼び出しだけで<code>backend</code>アプリケーションのなかに<code>BlogArticle</code>クラスの定義に基づく<code>article</code>モジュールが作成されるので、つぎのURLからアクセスできます:</p>

<pre><code>http://localhost/backend.php/article
</code></pre>

<p>図14-5、図14-6で描かれている生成モジュールの外見は商用アプリケーションとしてそのまま利用できるほど十分に洗練されています。</p>

<blockquote class="note"><p>
  administrationモジュールはRESTアーキテクチャに基づいています。<code>propel:generate-admin</code>タスクは<code>routing.yml</code>設定ファイルにルートなどを自動的に追加します:</p>

<pre class="yml"># apps/backend/config/routing.yml
article:
  class: sfPropelRouteCollection
  options:
    model:                BlogArticle
    module:               article
    with_wildcard_routes: true</pre>
  
  <p>独自のルートを作成しモデルクラスの名前の代わりに独自の名前を引数としてタスクに渡すこともできます:</p>

<pre><code>&gt; php symfony propel:generate-admin backend BlogArticle --module=article
</code></pre>
</blockquote>

<p>図14-5 - <code>backend</code>アプリケーション内部の<code>article</code>モジュールの<code>list</code>ビュー</p>

<p><img src="images/F1405.png" alt="backendアプリケーション内部のarticleモジュールのlistビュー" title="backendアプリケーション内部のarticleモジュールのlistビュー" /></p>

<p>図14-6 - バックエンドの<code>article</code>モジュールの<code>edit</code>ビュー</p>

<p><img src="images/F1406.png" alt="バックエンドのarticleモジュールのeditビュー" title="バックエンドのarticleモジュールのeditビュー" /></p>

<blockquote class="tip"><p>
  期待どおりの外見でなければ(スタイルシートと画像がない)、<code>plugin:publish-assets</code>タスクを実行してプロジェクトにアセットをインストールする必要があります:</p>

<pre><code>$ php symfony plugin:publish-assets
</code></pre>
</blockquote>

<a name="a.look.at.the.generated.code" id="a.look.at.the.generated.code"></a><h3>生成コードを見る</h3>

<p><code>apps/backend/modules/article/</code>ディレクトリ内の記事のadministrationのコードは初期化だけ行われたので空に見えます。このモジュールの生成コードを吟味するための最良の方法はブラウザーを利用してこれと情報のやりとりをすることと<code>cache/</code>フォルダーの内容を確認することです。リスト14-4はキャッシュのなかで見つかる生成アクションとテンプレートのリストを表示します。</p>

<p>リスト14-4 - 生成されたadministrationの要素(<code>cache/backend/ENV/modules/article/</code>)</p>

<pre><code>// actions/actions.class.phpのアクション
index            // テーブルのレコードのリストを表示する
filter           // リストで使われるフィルターを更新する
new              // 新しいレコードを作成するフォームを表示する
create           // 新しいレコードを作成する
edit             // レコードのフィールドを修正するフォームを表示する
update           // 既存のレコードを更新する
delete           // レコードを削除する
batch            // 選択されたレコードのリストでアクションを実行する

// templates/のなか
_assets.php
_filters.php
_filters_field.php
_flashes.php
_form.php
_form_actions.php
_form_field.php
_form_fieldset.php
_form_footer.php
_form_header.php
_list.php
_list_actions.php
_list_batch_actions.php
_list_field_boolean.php
_list_footer.php
_list_header.php
_list_td_actions.php
_list_td_batch_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
_pagination.php
editSuccess.php
indexSuccess.php
newSuccess.php
</code></pre>

<p>これは生成されたadministrationモジュールがおもに3つのビュー、<code>list</code>、<code>new</code>と<code>edit</code>で構成されることを示します。コードを見てみると、モジュール性が非常に高く、読みやすく拡張性のあるものであることがわかります。</p>

<a name="introducing.the.generator.yml.configuration.file" id="introducing.the.generator.yml.configuration.file"></a><h3>generator.yml設定ファイルを導入する</h3>

<p>生成されたadministrationモジュールはYAMLフォーマットの<code>generator.yml</code>設定ファイルで見つかるパラメーターに依存します。新しく生成されたadministrationモジュールのデフォルト設定を見るには、リスト14-5で再現されている、<code>backend/modules/article/config/</code>ディレクトリに設置された<code>generator.yml</code>ファイルを開いてください。</p>

<p>リスト14-5 - ジェネレーターのデフォルト設定(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre class="yml">generator:
  class: sfPropelGenerator
  param:
    model_class:           BlogArticle
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          article
    with_propel_route:     1
&nbsp;
    config:
      actions: ~
      list:    ~
      filter:  ~
      form:    ~
      edit:    ~
      new:     ~</pre>

<p>このコンフィギュレーションは基本的なadministrationを生成するのに十分です。カスタマイズした内容は<code>config</code>キーの下に追加されます。リスト14-6は<code>generator.yml</code>をカスタマイズした典型例を示しています。</p>

<p>リスト14-6 - 典型的なジェネレーターの全設定</p>

<pre class="yml">generator:
  class: sfPropelGenerator
  param:
    model_class:           BlogArticle
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          article
    with_propel_route:     1
&nbsp;
    config:
      actions:
        _new: { label: &quot;Create a new article&quot;, credentials: editor }
&nbsp;
      fields:
        author_id:    { label: Article author }
        published_on: { credentials: editor }
&nbsp;
      list:
        title:          Articles
        display:        [title, author_id, category_id]
        fields:
          published_on: { date_format: dd/MM/yy }
        layout:         stacked
        params:         |
          %%is_published%%&lt;strong&gt;%%=title%%&lt;/strong&gt;&lt;br /&gt;&lt;em&gt;by %%author%%
          in %%category%% (%%published_on%%)&lt;/em&gt;&lt;p&gt;%%content_summary%%&lt;/p&gt;
        max_per_page:   2
        sort:           [title, asc]
&nbsp;
      filter:
        display: [title, category_id, author_id, is_published]
&nbsp;
      form:
        display:
          &quot;Post&quot;:       [title, category_id, content]
          &quot;Workflow&quot;:   [author_id, is_published, created_on]
        fields:
          published_at: { help: &quot;Date of publication&quot; }
          title:        { attributes: { style: &quot;width: 350px&quot; } }
&nbsp;
      new:
        title:         New article
&nbsp;
      edit:
        title:          Editing article &quot;%%title%%&quot;</pre>

<p>このコンフィギュレーションでは、6つのセクションがあります。これらのうち4つはビューを表し(<code>list</code>、<code>filter</code>、<code>new</code>、と<code>edit</code>)とこれらのうちの2つは"バーチャル"(<code>fields</code>と<code>form</code>)で設定目的のためのみ存在します。</p>

<p>つぎのセクションでこの設定ファイルで利用可能なすべてのパラメーターの詳細内容を説明します。</p>

<a name="generator.configuration" id="generator.configuration"></a><h2>ジェネレーターの設定</h2>

<p>ジェネレーターの設定ファイルはとても強力で、生成されたadministrationを多くの方法で変更できます。しかしこの機能には代償があります: 全体の構文の記述が読んで学ぶには長いので、文章で説明したら、この章がこの本でもっとも長くなってしまいます。ですのでsymfonyのWebサイトはこの構文を学ぶための助けになる追加リソースを提示します: administrationジェネレーターのチートシートが図14-7で再現されてます。<a href="http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf">http://www.symfony-project.org/uploads/assets/sfAdminGeneratorRefCard.pdf</a>から保存し、この章のつぎの例を読むときにこれを頭のなかにとどめておいてください。</p>

<p>このセクションの例は、<code>BlogComment</code>クラスの定義に基づいて、administrationの<code>comment</code>モジュールと同様に、administrationの<code>article</code>モジュールを調整します。<code>propel:generate-admin</code>タスクを実行して後者を作成します:</p>

<pre><code>&gt; php symfony propel:generate-admin backend BlogComment --module=comment
</code></pre>

<p>図14-7 - administrationジェネレーターのチートシート</p>

<p><img src="images/F1407.png" alt="administrationジェネレーターのチートシート" title="administrationジェネレーターのチートシート" /></p>

<a name="fields" id="fields"></a><h3>フィールド</h3>

<p>デフォルトでは、<code>list</code>ビューのカラムは<code>schema.yml</code>で定義されるカラムです。<code>new</code>と<code>edit</code>ビューのフィールドはモデルに関連づけされたフォームで定義します(<code>BlogArticleForm</code>)。<code>generator.yml</code>によって、どのフィールドが表示され、どれが非表示であるかを選び、オブジェクトモデルで直接対応するものがなくても独自フィールドを追加できます。</p>

<a name="field.settings" id="field.settings"></a><h4>フィールドの設定</h4>

<p>administrationジェネレーターは<code>schema.yml</code>ファイルのカラムごとに1つのフィールドを作ります。<code>fields</code>キーの下で、それぞれのフィールドの表示方法やフォーマット方法などを修正できます。たとえば、リスト14-7で示されるフィールドの設定は<code>title</code>フィールド用のカスタムラベルクラスと入力タイプ、そして<code>content</code>フィールド用のラベルとツールチップを定義します。つぎのセクションでそれぞれのパラメーターが動作する方法を詳細に説明します。</p>

<p>リスト14-7 - カラムに対してカスタムラベルを設定する</p>

<pre class="yml">config:
  fields:
    title:
      label: Article Title
      attributes: { class: foo }
    content: { label: Body, help: Fill in the article body }</pre>

<p>リスト14-8でお手本が示されているように、すべてのビューに対するこのデフォルトの定義に加えて、任意のビュー(<code>list</code>、<code>filter</code>、<code>form</code>、<code>new</code>と<code>edit</code>)のためのフィールドの設定をオーバーライドできます。</p>

<p>リスト14-8 - ビュー単位でグローバルな設定ビューをオーバーライドする</p>

<pre class="yml">config:
  fields:
    title:     { label: Article Title }
    content:   { label: Body }
&nbsp;
  list:
    fields:
      title:   { label: Title }
&nbsp;
  form:
    fields:
      content: { label: Body of the article }</pre>

<p>これは一般的な原則です: <code>fields</code>キーの下のモジュール全体に対して設定される設定項目はビュー固有の領域でオーバーライドできます。オーバーライドのルールはつぎのとおりです:</p>

<ul>
<li><code>new</code>と<code>edit</code>は<code>form</code>を継承し<code>form</code>は<code>fields</code>を継承する</li>
<li><code>list</code>は<code>fields</code>を継承する</li>
<li><code>filter</code>は<code>fields</code>を継承する</li>
</ul>

<a name="adding.fields.to.the.display" id="adding.fields.to.the.display"></a><h4>display設定にフィールドを追加する</h4>

<p><code>fields</code>セクションで定義したフィールドはそれぞれのビューに対して表示、隠す、順番に並べるなど、さまざまな方法で分類できます。<code>display</code>キーはこの目的のために使われます。たとえば、<code>comment</code>モジュールのフィールドを順番に並べるには、リスト14-9のコードを使います。</p>

<p>リスト14-9 - 表示フィールドを選択する(<code>modules/comment/config/generator.yml</code>)</p>

<pre class="yml">config:
  fields:
    article_id: { label: Article }
    created_at: { label: Published on }
    content:    { label: Body }
&nbsp;
  list:
    display:    [id, article_id, content]
&nbsp;
  form:
    display:
      NONE:     [article_id]
      Editable: [author, content, created_at]</pre>

<p>図14-8のように<code>list</code>は3つのカラムを表示し、図14-9のように、<code>new</code>と<code>edit</code>フォームは2つのグループに集められた4つのフィールドを表示します。</p>

<p>図14-8 - <code>comment</code>モジュールの<code>list</code>ビュー内部のカスタムカラム設定</p>

<p><img src="images/F1408.png" alt="commentモジュールのlistビュー内部のカスタムカラム設定" title="commentモジュールのlistビュー内部のカスタムカラム設定" /></p>

<p>図14-9 - <code>comment</code>モジュールの<code>edit</code>ビュー内部でフィールドを分類する</p>

<p><img src="images/F1409.png" alt="commentモジュールのeditビュー内でフィールドを分類する" title="commentモジュールのeditビュー内でフィールドを分類する" /></p>

<p><code>display</code>設定を2つの方法で利用できます:</p>

<ul>
<li><code>list</code>ビューに対して: 表示するカラムと表示される順序を選ぶために単純な配列にフィールドを置きます。</li>
<li><code>form</code>、<code>new</code>、と<code>edit</code>ビューに対して: グループの名前をキーとするフィールドをグループ分けするのに連想配列、もしくは名前のないグループに対して<code>NONE</code>を使います。値はカラムの名前で並べ替えられた配列のままです。フォームクラスに参照されるすべての必須フィールドの一覧を表示することには注意を払ってください。さもないと予期せぬバリデーションエラーに遭遇するかもしれません。</li>
</ul>

<a name="custom.fields" id="custom.fields"></a><h4>カスタムフィールド</h4>

<p>当然のことながら、<code>generator.yml</code>で設定されたフィールドはスキーマで定義された実際のカラムに対応している必要はありません。関連クラスがカスタムゲッターを提供する場合、これを<code>list</code>ビューのためのフィールドとして使うことができます; ゲッターかつ/もしくはセッターが存在する場合、このクラスは<code>edit</code>ビューでも利用できます。たとえば、リスト14-10のように、<code>BlogArticle</code>モデルを<code>getNbComments()</code>メソッドで拡張できます。</p>

<p>リスト14-10 - モデルにカスタムゲッターを追加する(<code>lib/model/BlogArticle.class.php</code>)</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> getNbComments<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">countBlogComments</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>リスト14-11のように、<code>nb_comments</code>は生成モジュールのフィールドとして利用できます(ゲッターはcamelCaseバージョンのフィールド名を使います)。</p>

<p>リスト14-11 - カスタムゲッターはadministrationモジュール用のカラムを提供する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    display: [id, title, nb_comments, created_at]</pre>

<p><code>article</code>モジュールの<code>list</code>ビューの結果は図14-10で示されます。</p>

<p>図14-10 - <code>article</code>モジュールの<code>list</code>ビュー内のカスタムフィールド</p>

<p><img src="images/F1410.png" alt="articleモジュールのlistビュー内部のカスタムフィールド" title="articleモジュールのlistビュー内部のカスタムフィールド" /></p>

<p>カスタムフィールドは生のデータ以上のものを表示するためにHTMLのコードも返すことができます。たとえば、リスト14-12で示されるような<code>BlogComment</code>クラスを<code>getArticleLink()</code>メソッドで拡張できます。</p>

<p>リスト14-12 - HTMLを返すカスタムゲッターを追加する(<code>lib/model/BlogComment.class.php</code>)</p>

<pre class="yml">public function getArticleLink()
{
  return link_to($this-&gt;getBlogArticle()-&gt;getTitle(), 'article_edit', $this-&gt;getBlogArticle());
}</pre>

<p>リスト14-11と同じ構文でこの新しいゲッターを<code>comment/list</code>ビュー内のカスタムフィールドとして使うことができます。リスト14-13の例と、図14-11の結果をご覧ください。ゲッター(記事のハイパーリンク)によって出力されるHTMLのコードは記事の主キーの代わりに2番目のカラムに現れます。</p>

<p>リスト14-13 - HTMLの結果を返すカスタムゲッターは追加カラムとしても使える(<code>modules/comment/config/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    display: [id, article_link, content]</pre>

<p>図14-11 - <code>comment</code>モジュールの<code>list</code>ビュー内部のカスタムフィールド</p>

<p><img src="images/F1411.png" alt="commentモジュールのlistビュー内部のカスタムフィールド" title="commentモジュールのlistビュー内部のカスタムフィールド" /></p>

<a name="partial.fields" id="partial.fields"></a><h4>部分テンプレートフィールド</h4>

<p>モデルに設置されたコードはプレゼンテーションから独立していなければなりません。前出の<code>getArticleLink()</code>メソッドの例はレイヤー分離の原則を順守していません。ビューコードのなかにはモデルレイヤーに現れるものがあるからです。正しい方法で、同じ結果を実現するには、カスタムフィールドに対応するHTMLを出力するコードは部分テンプレートで対応するほうがベターです。幸いにして、administrationジェネレーターによってアンダースコアのプレフィックスを持つフィールド名を宣言できます。この場合、リスト14-13の<code>generator.yml</code>ファイルはリスト14-14のように修正されます。</p>

<p>リスト14-14 - プレフィックスの<code>_</code>を使うことで部分テンプレートを追加カラムとして使うことができる、</p>

<pre class="yml">config:
  list:
    display: [id, _article_link, created_at]</pre>

<p>これを機能させるには、リスト14-15で示されるように、<code>modules/comment/tempaltes/</code>ディレクトリのなかで<code>_article_link.php</code>部分テンプレートを作らなければなりません。</p>

<p>リスト14-15 - <code>list</code>ビュー用の部分テンプレートの例(<code>modules/comment/templates/_article_link.php</code>)</p>

<pre class="yml">&lt;?php echo link_to($form-&gt;getObject()-&gt;getBlogArticle()-&gt;getTitle(), '@article_edit', $form-&gt;getObject()-&gt;getBlogArticle()) ?&gt;</pre>

<p>部分テンプレートフィールドの部分テンプレートテンプレートはクラスによって命名された変数(この例では<code>$comment</code>)を通して現在のオブジェクトにアクセスできることに注意してください。たとえば、<code>UserGroup</code>という名前のクラスのためにビルドされたモジュールに対して、部分テンプレートは<code>$user_group</code>変数を通して現在のオブジェクトにアクセスできるようになります。</p>

<p>レイヤー分離の原則が順守されることを除いて、結果は図14-11と同じです。レイヤー分離の原則を順守することに慣れたら、アプリケーションはより維持しやすくなります。</p>

<p>部分テンプレートフィールドのパラメーターをカスタマイズする必要がある場合、<code>field</code>キーの下で通常のフィールドと同じ事を行います。アンダースコア(<code>_</code>)をキーの先頭に含めないでください。リスト14-16をご覧ください。</p>

<p>リスト14-16 - 部分テンプレートフィールドのプロパティは<code>fields</code>キーの下でカスタマイズできる</p>

<pre class="yml">config:
  fields:
    article_link: { label: Article }</pre>

<p>部分テンプレートにロジックが混在するようになったら、おそらくはこれをコンポーネントで置き換えたいと思うでしょう。リスト14-17のように、プレフィックスの<code>_</code>を<code>~</code>に変更することで、コンポーネントフィールドを定義できます。</p>

<p>リスト14-17 - コンポーネントは追加カラムとして利用できる。プレフィックスの<code>~</code>を使う</p>

<pre class="yml">config:
  list:
    display: [id, ~article_link, created_at]</pre>

<p>生成テンプレートにおいて、現在のモジュールの<code>articleLink</code>コンポーネントを呼び出すことでこれは生成されます。</p>

<blockquote class="note"><p>
  カスタムと部分テンプレートフィールドは<code>list</code>、<code>new</code>、<code>edit</code>と<code>filter</code>ビューのなかで使うことができます。複数のビューに対して同じ部分テンプレートを使う場合、内容(<code>list</code>、<code>new</code>、<code>edit</code>、もしくは<code>filter</code>)は変数<code>$type</code>に保存されます。</p>
</blockquote>

<a name="view.customization" id="view.customization"></a><h3>ビューのカスタマイゼーション</h3>

<p><code>new</code>、<code>edit</code>と<code>list</code>ビューの外見を変更するために、テンプレートを変更したいと思うことがあります。しかしこれらは自動的に生成されるので、これを行うのはあまりよいアイディアではありません。代わりに<code>generator.yml</code>設定ファイルを使うべきです。モジュール性を損なうことなくほとんどすべての必要なことを行うことができるからです。</p>

<a name="changing.the.view.title" id="changing.the.view.title"></a><h4>ビューのタイトルを変更する</h4>

<p>フィールドのカスタムセットに加えて、<code>list</code>、<code>new</code>、<code>edit</code>ページはカスタムページタイトルを持ちます。たとえば、<code>article</code>ビューのタイトルをカスタマイズしたい場合、リスト14-18のように行います。<code>edit</code>ビューの結果は図14-12に描かれています</p>

<p>リスト14-18 - それぞれのビューに対してカスタムタイトルを設定する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    title: List of Articles
&nbsp;
  new:
    title: New Article
&nbsp;
  edit:
    title: Edit Article %%title%% (%%id%%)</pre>

<p>図14-12 - <code>article</code>モジュールの<code>edit</code>ビュー内部のカスタムタイトル</p>

<p><img src="images/F1412.png" alt="articleモジュールのeditビュー内部のカスタムタイトル" title="articleモジュールのeditビュー内部のカスタムタイトル" /></p>

<p>デフォルトのタイトルはクラスの名前を使うので、モデルが明白なクラスの名前を使うのであれば、これらのクラス名で十分であることはよくあります。</p>

<blockquote class="tip"><p>
  <code>generator.yml</code>の文字列の値において、フィールドの値は<code>%%</code>で囲まれたフィールドの名前を通してアクセスできます。</p>
</blockquote>

<a name="adding.tooltips" id="adding.tooltips"></a><h4>ツールチップを追加する</h4>

<p><code>list</code>、<code>new</code>、<code>edit</code>、と<code>filter</code>ビュー内部において、表示されるフィールドを記述するための助けになるツールチップを追加できます。たとえば、ツールチップを<code>comment</code>モジュールの<code>edit</code>ビューの<code>article_id</code>フィールドに追加するには、リスト14-19のように、<code>fields</code>の定義に<code>help</code>プロパティを追加します。結果は図14-13に示されています。</p>

<p>リスト14-19 - <code>edit</code>ビューでツールチップを設定する(<code>modules/comment/config/generator.yml</code>)</p>

<pre class="yml">config:
  edit:
    fields:
      article_id: { help: The current comment relates to this article }</pre>

<p>図14-13 - <code>comment</code>モジュールの<code>edit</code>ビュー内部のツールチップ</p>

<p><img src="images/F1413.png" alt="commentモジュールのeditビュー内部のツールチップ" title="commentモジュールのeditビュー内部のツールチップ" /></p>

<p><code>list</code>ビューにおいて、ツールチップはカラムのヘッダーに表示されます; <code>new</code>、<code>edit</code>、<code>filter</code>ビューにおいてこれらはfieldタグの下で現れます。</p>

<a name="modifying.the.date.format" id="modifying.the.date.format"></a><h4>日付の書式を修正する</h4>

<p>リスト14-20でお手本が示されているように、<code>date_format</code>オプションを使うことで同時にカスタム書式で日付を表示できます。</p>

<p>リスト14-20 <code>list</code>ビューのなかで日付の書式設定をする</p>

<pre class="yml">config:
  list:
    fields:
      created_at: { label: Published, date_format: dd/MM }</pre>

<p>このパラメーターは以前の章で説明された<code>format_date()</code>ヘルパーと同じフォーマットパラメーターを受けとります。</p>

<blockquote class="sidebar"><p class="title">
  administrationテンプレートは国際化の準備ができています</p>
  
  <p>adminで生成されたモジュールはインターフェイスの文字列(デフォルトのアクションの名前、ページ分割のヘルプメッセージ、・・・)とカスタム文字列(タイトル、カラムのラベル、ヘルプメッセージ、エラーメッセージ、・・・)で構成されます。</p>
  
  <p>インターフェイスの文字列の多言語翻訳機能はsymfonyに搭載されています。しかし独自のものを追加するか<code>sf_admin</code>カタログ(<code>apps/frontend/i18n/sf_admin.XX.xml</code>、<code>XX</code>は言語のISOコード)用の<code>i18n</code>ディレクトリでカスタムXLIFFファイルを作成することで既存のものをオーバーライドできます</p>
  
  <p>生成されたテンプレートで見つかるすべてのカスタム文字列も自動的に国際化されます(すなわち、<code>__()</code>ヘルパーーへの呼び出しと一緒に)。このことは前の章で説明したように、<code>apps/frontend/i18n/</code>ディレクトリでXLIFFファイルにフレーズの翻訳を追加することで、生成されたadministrationを簡単に翻訳できることを意味します。</p>
  
  <p><code>i18n_catalogue</code>パラメーターを指定することでカスタム文字列用に使われるデフォルトのカタログを変更できます:</p>

<pre class="yml">generator:
  class: sfPropelGenerator
  param:
    i18n_catalogue: admin</pre>
</blockquote>

<a name="list.viewspecific.customization" id="list.viewspecific.customization"></a><h3>listビュー固有のカスタマイゼーション</h3>

<p><code>list</code>ビューは、表形式、もしくはすべての詳細な内容が一行に積み重ねられた状態で、レコードの詳細を表示できます。これはフィルター、ページ分割とソート機能も含みます。これらの機能は設定によって変更できます。詳細はつぎのセクションで説明します。</p>

<a name="changing.the.layout" id="changing.the.layout"></a><h4>レイアウトを変更する</h4>

<p>デフォルトでは、<code>list</code>ビューと<code>edit</code>ビュー間のハイパーリンクは主キーのカラムによって生成されます。図14-11に戻ると、コメントリストの<code>id</code>カラムがそれぞれのコメントの主キーを表示するだけでなく、ユーザーが<code>edit</code>ビューにアクセスすることを許可するハイパーリンクを提供します。</p>

<p>別のカラムに現れるレコードの詳細内容へのハイパーリンクが望ましいのであれば、<code>display</code>キーの等号(<code>=</code>)をプレフィックスとしてカラムの名前に追加します。リスト14-21は<code>list</code>コメントの表示フィールドから<code>id</code>を除去して代わりに<code>content</code>フィールドにハイパーリンクを置く方法を示しています。図14-14のスクリーンショットを確認してください。</p>

<p>リスト14-21 - <code>edit</code>ビューのためのハイパーリンクを<code>list</code>ビューに移動させる(<code>modules/comment/config/gererator.yml</code>)</p>

<pre class="yml">config:
  list:
    display:    [article_link, =content]</pre>

<p>図14-14 - <code>comment</code>モジュールの<code>list</code>ビューのなかで、リンクを別のカラム上の<code>edit</code>ビューに移動させる</p>

<p><img src="images/F1414.png" alt="commentモジュールのlistビュー内で、リンクを別のカラム上のeditビューに移動させる" title="commentモジュールのlistビュー内で、リンクを別のカラム上のeditビューに移動させる" /></p>

<p>デフォルトでは、以前示されたように、<code>list</code>ビューはフィールドがカラムとして表示される<code>tabular</code>レイアウトを使います。しかし<code>stacked</code>レイアウトを使ってフィールドをテーブルの全長を詳しく記述する単独の文字列に連結することもできます。<code>stacked</code>レイアウトを選ぶ場合、<code>params</code>キーにリストのそれぞれの行の値を定義するパターンを組み込まなければなりません。たとえば、リスト14-22は<code>comment</code>モジュールの<code>list</code>ビューに対して<code>stacked</code>レイヤーを定義します。結果は図14-15です。</p>

<p>リスト14-22 - <code>list</code>ビューで<code>stacked</code>レイアウトを使う(<code>modules/comment/cofig/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    layout:  stacked
    params:  |
      %%=content%%&lt;br /&gt;
      (sent by %%author%% on %%created_at%% about %%article_link%%)
    display:  [created_at, author, content]</pre>

<p>図14-15 - <code>comment</code>モジュールの<code>list</code>ビュー内部のstackedレイアウト</p>

<p><img src="images/F1415.png" alt="commentモジュールのlistビュー内部のstackedレイアウト" title="commentモジュールのlistビュー内のstackedレイアウト" /></p>

<p><code>tabular</code>レイアウトは<code>display</code>キーの下でフィールドの配列を必要としますが、<code>stacked</code>レイアウトはそれぞれのレコードに対して生成されたHTMLコードのために<code>params</code>キーを使うことに留意してください。しかしながら、インタラクティブなソートに対して利用できるカラムヘッダーを決定するために<code>display</code>配列が<code>stacked</code>レイアウトでも使われます</p>

<a name="filtering.the.results" id="filtering.the.results"></a><h4>結果をフィルタリングする</h4>

<p><code>list</code>ビューにおいて、フィルターのインタラクションのセットを追加できます。これらのフィルターによって、ユーザーは結果をより少なく表示して速く望むものに到達できます。フィールド名の配列で、<code>filter</code>キーの下にフィルターを設定します。たとえば、図14-16のフィルターボックスと同じようなものを表示するには、リスト14-23のように、<code>article_id</code>、<code>author</code>フィールド、と<code>created_at</code>フィールド上のフィルターをコメントの<code>list</code>ビューに追加します。これを機能させるには、<code>BlogArticle</code>クラスに<code>__toString()</code>メソッドを追加する必要があります(たとえば、記事の<code>title</code>を返す)。</p>

<p>リスト14-23 - <code>list</code>ビューでフィルターを設定する(<code>modules/comment/config/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    layout:  stacked
    params:  |
      %%=content%% &lt;br /&gt;
      (sent by %%author%% on %%created_at%% about %%article_link%%)
    display:  [created_at, author, content]
&nbsp;
  filter:
    display: [article_id, author, created_at]</pre>

<p>図14-16 - <code>comment</code>モジュールの<code>list</code>ビュー内部のフィルター</p>

<p><img src="images/F1416.png" alt="commentモジュールのlistビュー内部のフィルター" title="commentモジュールのlistビュー内部のフィルター" /></p>

<p>symfonyに表示されるフィルターはスキーマで定義されるカラムの型に依存し、フィルターフォームクラスでカスタマイズできます:</p>

<ul>
<li>テキストカラム(たとえば<code>comment</code>モジュール内の<code>author</code>フィールド)に対して、フィルターはテキストベースの検索を可能にするテキスト入力です(ワイルドカードが自動的に追加).</li>
<li>外部キー(たとえば<code>comment</code>モジュール内部の<code>article_id</code>フィールドのリスト)に対して、フィルターは関連するテーブルのレコードのドロップダウンリストです。デフォルトでは、ドロップダウンリストのオプションは関連するクラスの<code>__toString()</code>メソッドによって返されるものです。</li>
<li>日付のカラム(たとえば<code>comment</code>カラム内の<code>created_at</code>フィールド)に対して、フィルターはリッチな日付タグのペアで、時間の間隔を選択できるようにします。</li>
<li>ブール型のカラムに対して、フィルターは<code>true</code>と<code>false</code>と<code>true or false</code>オプションを持つドロップダウンリストです。最後の値はフィルターを再初期化します。</li>
</ul>

<p><code>new</code>と<code>edit</code>ビューがフォームクラスに結びつけられているように、モデルに関連するデフォルトのフィルターフォームクラスです(たとえば<code>BlogArticle</code>モデルに対して<code>BlogArticleFormFilter</code>)。フィルターフォームに対してカスタムクラスを定義することで、フォームフレームワークの力を活用しすべての利用可能なフィルターウィジェットを使うことでフィルターフィールドをカスタマイズできます。リスト14-24で示されるように、<code>filter</code>エントリーの下で<code>class</code>を定義することで簡単に実現できます。</p>

<p>リスト14-24 - フィルタリングに使われるフォームクラスをカスタマイズする</p>

<pre class="yml">config:
  filter:
    class: BackendArticleFormFilter</pre>

<blockquote class="tip"><p>
  フィルターを一緒に無効にするには、フィルター用の<code>class</code>に<code>false</code>を指定するだけです。</p>
</blockquote>

<p>フィルターを修正するために部分テンプレートフィルターも使うことができます。それぞれの部分テンプレートはフォームをレンダリングするさいに<code>form</code>とHTMLの<code>attributes</code>を受けとります。リスト14-25は部分テンプレート以外のデフォルトのふるまいを模倣する実装の例を示しています。</p>

<p>リスト14-25 - 部分テンプレートフィルターを使う</p>

<pre class="yml">// templates/_state.phpで、部分テンプレートを定義する
&lt;?php echo $form[$name]-&gt;render($attributes-&gt;getRawValue()) ?&gt;
&nbsp;
// config/generator.ymlで部分テンプレートフィルターのリストを追加する
config:
  filter: [date, _state]</pre>

<a name="sorting.the.list" id="sorting.the.list"></a><h4>リストをソートする</h4>

<p><code>list</code>ビューにおいて、図14-18で示されるように、テーブルのヘッダーはリストを再び順番に並べるために使われるハイパーリンクです。これらのヘッダーは<code>tabular</code>レイアウトと<code>stacked</code>レイアウトの両方で表示されます。これらのリンクをクリックするとリストの順番を再配置する<code>sort</code>パラメーターでページがリロードされます。</p>

<p>図14-18 - <code>list</code>ビューのテーブルヘッダーはソートをコントロールする</p>

<p><img src="images/F1418.png" alt="listビューのテーブルヘッダーはソートはコントロールする" title="listビューのテーブルヘッダーはソートをコントロールする" /></p>

<p>あるひとつのカラムによってソートされたリストを直接指定する構文を再利用できます:</p>

<pre class="yml">&lt;?php echo link_to('日付ごとのコメントのリスト', '@comments?sort=created_at&amp;sort_type=desc' ) ?&gt;</pre>

<p><code>generator.yml</code>ファイル内部の<code>list</code>ビューに対して直接デフォルトの<code>sort</code>の順番を定義することも可能です。構文はリスト14-26で示された例に従います。</p>

<p>リスト14-26 - <code>list</code>ビューのなかでデフォルトの<code>sort</code>フィールドを設定する</p>

<pre class="yml">config:
  list:
    sort:   created_at
    # ソートの順序を指定するための、代替構文
    sort:   [created_at, desc]</pre>

<blockquote class="note"><p>
  実際のカラムに対応するフィールドだけが、ソートのコントロール機能に変換されます。カスタムもしくは部分テンプレートフィールドには対応していません。</p>
</blockquote>

<a name="customizing.the.pagination" id="customizing.the.pagination"></a><h4>ページ分割をカスタマイズする</h4>

<p>生成されたadministrationは大きなテーブルさえも効率的に処理します。<code>list</code>ビューがデフォルトでページ分割を使うからです。テーブル内の実際の列の数がページごとの列の最大数を越えるとき、ページ分割のコントロール機能がリストの底に現れます。たとえば、図14-19はページごとに表示される5つのコメントの制限ではなく、テーブル内で6つのテストのコメントを持つコメントのリストを表示しますが、ページごとに表示されるコメント数は最大5まで制限されています。ページ分割はよいパフォーマンスとユーザービリティを保証します。データベースから表示される列のみが効率的に検索され、administrationモジュールによって何百万の列をかかえるテーブルでさえも管理できるからです。</p>

<p>図14-19 - ページ分割のコントロール機能は長いリスト上に現れる</p>

<p><img src="images/F1419.png" alt="ページ分割のコントロール機能は長いリスト上に現れる" title="ページ分割のコントロール機能は長いリスト上に現れる" /></p>

<p><code>max_per_page</code>パラメーターによってそれぞれのページが表示されるレコードの数をカスタマイズできます:</p>

<pre class="yml">config:
  list:
    max_per_page:   5</pre>

<a name="using.a.join.to.speed.up.page.delivery" id="using.a.join.to.speed.up.page.delivery"></a><h4>ページ配信を加速するためにJoinを使う</h4>

<p>デフォルトでは、administrationジェネレーターはレコードのリストを検索する<code>doSelect()</code>を使います。しかし、リストで関連オブジェクトを使う場合、リストを表示するために必要なデータベースクエリの数が急に増えることがあります。たとえば、コメントのリストで記事の名前を表示したい場合、関連する<code>BlogArticle</code>オブジェクトを検索するためにリスト内のそれぞれの投稿に対する追加クエリが必要です。ですので、クエリの数を最適化するために、<code>doSelectJoinXXX()</code>メソッドを使うページャーを強制したい場合があるかもしれません。これは<code>peer_method</code>パラメーターで指定できます。</p>

<pre class="yml">config:
  list:
    peer_method: doSelectJoinBlogArticle</pre>

<p>18章ではJoinの概念についてより広範囲に説明します。</p>

<a name="new.and.edit.viewspecific.customization" id="new.and.edit.viewspecific.customization"></a><h3>newとeditビュー固有のカスタマイズ</h3>

<p><code>new</code>もしくは<code>edit</code>ビューにおいて、ユーザーは新しいレコードもしくは渡されたレコードに対してそれぞれのカラムの値を修正できます。デフォルトでは、adminジェネレーターで使われるフォームはモデル:<code>BlogArticle</code>モデルに対して<code>BlogArticleForm</code>に関連するフォームです。リスト14-27で示されるように<code>form</code>エントリーの下で<code>class</code>を定義することで使うクラスをカスタマイズできます。</p>

<p>リスト14-27 - <code>new</code>と<code>edit</code>ビューに使われるフォームクラスをカスタマイズする</p>

<pre class="yml">config:
  form:
    class: BackendBlogArticleForm</pre>

<p>カスタムフォームクラスを利用することでadminジェネレーター用のウィジェットとバリデーターすべてをカスタマイズできます。frontendアプリケーションに対してデフォルトのフォームクラスが使われ特別にカスタマイズされます。</p>

<p>リスト14-28で示されるように<code>generator.yml</code>設定ファイルでラベル、ヘルプメッセージ、とフォームのレイアウトを直接カスタマイズすることもできます。</p>

<p>リスト14-28 - フォーム表示をカスタマイズする</p>

<pre class="yml">config:
  form:
    display:
      NONE:     [article_id]
      Editable: [author, content, created_at]
    fields:
      content:  { label: body, help: &quot;The content can be in the Markdown format&quot; }</pre>

<a name="handling.partial.fields" id="handling.partial.fields"></a><h4>部分テンプレートフィールドを扱う</h4>

<p>部分テンプレートフィールドは<code>list</code>ビューのように<code>new</code>と<code>edit</code>ビューで扱えます。</p>

<a name="dealing.with.foreign.keys" id="dealing.with.foreign.keys"></a><h3>外部キーを扱う</h3>

<p>スキーマがテーブルのリレーションを定義する場合、生成されたadministrationモジュールはこれを活用して、より自動化されたコントロール方法を提供するので、リレーションの管理作業が大いに簡略化されます。</p>

<a name="onetomany.relationships" id="onetomany.relationships"></a><h4>一対多のリレーション</h4>

<p>1対多のテーブルのリレーションはadministrationジェネレーターによって考慮されます。以前の図14-1で記述されたように、<code>blog_comment</code>テーブルは<code>article_id</code>フィールドを通して<code>blog_article</code>テーブルとの関係を持ちます。<code>BlogComment</code>クラスのモジュールをadministrationジェネレーターによって初期化する場合、<code>edit</code>ビューは<code>blog_article</code>テーブル内の利用可能なレコードのIDを示す<code>article_id</code>をドロップダウンリストとして表示します(説明図は図14-9を再度確認)。</p>

<p>加えて、<code>BlogArticle</code>オブジェクトで<code>__toString()</code>メソッドを定義する場合、ドロップダウンオプションのテキストは主キーの代わりにこのメソッドを使います。</p>

<p><code>article</code>モジュール(多対一のリレーション)内部の記事に関連するコメントのリストを表示する場合、部分テンプレートフィールドの方法によってモジュールを少しカスタマイズする必要があります。</p>

<a name="manytomany.relationships" id="manytomany.relationships"></a><h4>多対多のリレーション</h4>

<p>symfonyは図14-20で示されるように多対多のリレーションも考慮します。</p>

<p>図14-20 - 多対多のリレーション</p>

<p><img src="images/F1420.png" alt="多対多のリレーション" title="多対多のリレーション" /></p>

<p>リレーションをレンダリングするために使われるウィジェットをカスタマイズすることで、フィールドのレンダリングを調整できます(図14-21で説明):</p>

<p>図14-21 - 多対多のリレーションに対して利用できるコントロール機能</p>

<p><img src="images/F1421.png" alt="多対多のリレーションに対して利用できるコントロール機能" title="多対多のリレーションに対して利用できるコントロール機能" /></p>

<a name="adding.interactions" id="adding.interactions"></a><h3>インタラクションを追加する</h3>

<p>administrationモジュールはユーザーが通常のCRUDオペレーションを実行できるようにしますが、ビューに対して独自のインタラクションを追加するもしくは可能なインタラクションを制限することもできます。たとえば、リスト14-31で示されているインタラクションを定義することで<code>article</code>モジュール上のすべてのCRUDアクションにデフォルトでアクセスできるようになります。</p>

<p>リスト14-31 - それぞれのビューに対してインタラクションを定義する(<code>backend/modules/article/config/generator.yml</code>)</p>

<pre class="yml">config:
  list:
    title:          List of Articles
    object_actions:
      _edit:         ~
      _delete:       ~
    batch_actions:
      _delete:       ~
    actions:
      _new:          ~
&nbsp;
  edit:
    title:          Body of article %%title%%
    actions:
      _delete:       ~
      _list:         ~
      _save:         ~
      _save_and_add: ~</pre>

<p><code>list</code>ビューにおいて、アクションの設定が3つ存在します: すべてのオブジェクトに対して利用可能なアクション(<code>object_actions</code>)、オブジェクトの選択に対して利用可能なアクション(<code>batch_actions</code>)、ページ全体に対して利用可能なアクション(<code>actions</code>)です。リスト14-31で定義されたリストのインタラクションは図14-22のようにレンダリングします。それぞれの行はレコードを編集するためのボタンとレコードを削除するためのボタン、それらに加えて、レコードの選択を削除するためにそれぞれの行の上に1つのチェックボックスを表示します。リストの一番下で、1つのボタンによって新しいレコードを作ることができます。</p>

<p>図14-22 - <code>list</code>ビュー内部のインタラクション</p>

<p><img src="images/F1422.png" alt="listビュー内部のインタラクション" title="listビュー内部のインタラクション" /></p>

<p><code>new</code>と<code>edit</code>ビューにおいて、一度に編集されるレコードは1つだけであり、(<code>actions</code>のもとで)定義するアクションのセットは1つだけです。リスト14-31で定義された<code>edit</code>インタラクションは図14-23のようにレンダリングされます。<code>save</code>アクションと<code>save_and_add</code>アクションは現在の編集をレコードに保存します。これらのアクションの違いは、<code>save</code>アクションは保存したあとで現在のレコード上に<code>edit</code>ビューを表示するのに対して、<code>save_adn_add</code>アクションは別のレコードを追加するために<code>new</code>ビューを表示することです。<code>save_and_add</code>アクションは続けざまに多くのレコードを追加するときに非常に便利なショートカットです。<code>delete</code>アクションの位置に関しては、これはほかのボタンから分離されているので、ユーザーが誤ってクリックすることはありません。</p>

<p>アンダースコア(<code>_</code>)で始まるインタラクション名はsymfonyに対してこれらのアクションに対応するデフォルトのアイコンとアクションを使うように伝えます。administrationジェネレーターは<code>_edit</code>、<code>_delete</code>、<code>_new</code>、<code>_list</code>、<code>_save</code>、<code>_save_and_add</code>、と<code>_create</code>を理解します。</p>

<p>図14-23 - <code>edit</code>ビュー内部のインタラクション</p>

<p><img src="images/F1423.png" alt="editビュー内部のインタラクション" title="editビュー内部のインタラクション" /></p>

<p>しかしカスタムインタラクションを追加することもできます。この場合リスト14-32で示されるように、アンダースコアで始まらない名前、と現在のモジュールのなかのターゲットのアクションを指定しなければなりません。</p>

<p>リスト14-32 - カスタムインタラクションを定義する</p>

<pre class="yml">list:
  title:          List of Articles
  object_actions:
    _edit:        -
    _delete:      -
    addcomment:   { label: Add a comment, action: addComment }</pre>

<p>図14-24で示されるように、リストにおけるそれぞれのアクションは<code>Add a comment</code>リンクを表示します。これをクリックすることで現在のモジュール内で<code>addComment</code>アクションを呼び出すことが行われます。現在のオブジェクトの主キーはリクエストパラメーターに自動的に追加されます。</p>

<p>図14-24 - <code>list</code>ビュー内部のカスタムインタラクション</p>

<p><img src="images/F1424.png" alt="listビュー内部ののカスタムインタラクション" title="listビュー内部のカスタムインタラクション" /></p>

<p><code>addComment</code>アクションはリスト14-33のように実装できます。</p>

<p>リスト14-33 - カスタムインタラクションアクション(<code>actions/actions.class.php</code>)</p>

<pre class="yml">public function executeAddComment($request)
{
  $comment = new BlogComment();
  $comment-&gt;setArticleId($request-&gt;getParameter('id'));
  $comment-&gt;save();
&nbsp;
  $this-&gt;redirect('comments_edit', $comment);
}</pre>

<p>バッチスクリプトは<code>sf_admin_batch_selection</code>リクエストパラメーターのなかで選択されたレコードの主キーの配列を受けとります。</p>

<p>アクションについて最後の一言です: あるカテゴリのためにアクションを完全に抑制したい場合、リスト14-34で示されるように、空のリストを使います。</p>

<p>リスト14-34 - <code>list</code>ビューのすべてのアクションを除去する</p>

<pre class="yml">config:
  list:
    title:   List of Articles
    actions: {}</pre>

<a name="form.validation" id="form.validation"></a><h3>フォームのバリデーション</h3>

<p>バリデーションは<code>new</code>と<code>edit</code>ビューで使われるフォームを自動的に考慮します。対応するフォームクラスを編集することでこのフォームをカスタマイズできます。</p>

<a name="restricting.user.actions.using.credentials" id="restricting.user.actions.using.credentials"></a><h3>クレデンシャルを利用してユーザーのアクションを制限する</h3>

<p>特定のadministrationモジュールに対して、利用可能なフィールドとインタラクションはログインユーザーのクレデンシャルによって変化します(symfonyのセキュリティ機能の説明に関しては6章を参照)。</p>

<p>適切なクレデンシャルを持つユーザーのみに対して表示されるようにするためにジェネレーター内部のフィールドは<code>credentials</code>パラメーターを考慮に入れます。これは<code>list</code>エントリーに対して機能します。加えて、ジェネレーターはクレデンシャルにしたがってインタラクションも隠すことができます。リスト14-37はこれらの機能のお手本を示しています。</p>

<p>リスト14-37 - クレデンシャルを使う(<code>generator.yml</code>)</p>

<pre class="yml">config:
  # adminクレデンシャルを持つユーザーに対してのみidカラムは表示される
  list:
    title:          List of Articles
    display:        [id, =title, content, nb_comments]
    fields:
      id:           { credentials: [admin] }
&nbsp;
  # addcommentインタラクションはadminクレデンシャルを持つユーザーに制限される
  actions:
    addcomment: { credentials: [admin] }</pre>

<a name="modifying.the.presentation.of.generated.modules" id="modifying.the.presentation.of.generated.modules"></a><h2>生成モジュールのプレゼンテーションを修正する</h2>

<p>独自のスタイルシートを適用するだけでなく、デフォルトのテンプレートで上書きすることで、生成されたモジュールのプレゼンテーションが既存のグラフィカルな表にマッチするように、そのモジュールを修正できます。</p>

<a name="using.a.custom.style.sheet" id="using.a.custom.style.sheet"></a><h3>カスタムスタイルシートを使う</h3>

<p>生成されたHTMLコードは構造化された内容を持つので、プレゼンテーションであなたが望むことを大いに行うことができます。</p>

<p>リスト14-38で示されるように、<code>css</code>パラメーターを生成されたジェネレーターの設定に追加することで、administrationモジュールに対して、デフォルトの代わりにカスタムCSSを定義できます。</p>

<p>リスト14-38 - デフォルトの代わりにカスタムスタイルシートを使う</p>

<pre class="yml">class: sfPropelGenerator
param:
  model_class:           BlogArticle
  theme:                 admin
  non_verbose_templates: true
  with_show:             false
  singular:              ~
  plural:                ~
  route_prefix:          article
  with_propel_route:     1
  css:                   mystylesheet</pre>

<p>もう一つの方法として、ビュー単位でスタイルを上書きするためにモジュールの<code>view.yml</code>によって提供されるメカニズムも利用できます。</p>

<a name="creating.a.custom.header.and.footer" id="creating.a.custom.header.and.footer"></a><h3>カスタムヘッダーとフッターを生成する</h3>

<p><code>list</code>、<code>new</code>、<code>edit</code>ビューはヘッダーとフッター部分テンプレートを系統的に含むことができます。administrationモジュールの<code>templates/</code>ディレクトリのなかにはそのような部分テンプレートは存在しませんが、部分テンプレートを自動的にインクルードするには以下の名前の1つを追加する必要があります:</p>

<pre><code>_list_header.php
_list_footer.php
_form_header.php
_form_footer.php
</code></pre>

<p>たとえば、カスタムヘッダーを<code>article/edit</code>ビューに追加したい場合、リスト14-39のように<code>_form_header.php</code>という名前のファイルを作ります。これは追加の設定なしで動作します。</p>

<p>リスト14-39 - <code>edit</code>ヘッダー部分テンプレートの例(<code>modules/article/templates/_form_header.php</code>)</p>

<pre class="yml">&lt;?php if ($article-&gt;getNbComments() &gt; 0): ?&gt;
  &lt;h2&gt;この記事には&lt;?php echo $article-&gt;getNbComments() ?&gt;のコメントがあります&lt;/h2&gt;
&lt;?php endif; ?&gt;</pre>

<p><code>edit</code>部分テンプレートはクラスによって命名された変数を通していつでも現在のオブジェクトにアクセス可能で、<code>list</code>部分テンプレートは<code>$pager</code>変数を通していつでも現在のページャーにアクセスできることに留意してください。</p>

<blockquote class="sidebar"><p class="title">
  administrationアクションをカスタムパラメーターで呼び出す</p>
  
  <p>administrationモジュールアクションは<code>link_to()</code>ヘルパーの<code>query_string</code>引数を使ってカスタムパラメーターを受けとることができます。たとえば、記事に対するコメントへのリンクを持つ<code>previous_edit_header</code>部分テンプレートを拡張するには、つぎのように書きます:</p>
</blockquote>

<pre class="yml">&lt;?php if ($article-&gt;getNbComments() &gt; 0): ?&gt;
  &lt;h2&gt;この記事には&lt;?php echo link_to($article-&gt;getNbComments().'のコメント', 'comment/list', array('query_string' =&gt; 'filter=filter&amp;filters%5Barticle_id%5D='.$article-&gt;getId())) ?&gt;があります&lt;/h2&gt;
&lt;?php endif; ?&gt;</pre>

<blockquote class="sidebar"><p class="title"></p>
  
  <p>このクエリの文字列パラメーターはより読みやすいエンコードされたバージョンです。</p>

<pre class="yml">'filter=filter&amp;filters[article_id]='.$article-&gt;getId()</pre>
  
  <p>これは<code>$article</code>に関連するコメントだけを表示するためにコメントをフィルタリングします。<code>query_string</code>引数を使うことで、ソートの順番かつ/またはカスタムlistビューを表示するフィルターを指定することもできます。これはカスタムインタラクションに対しても便利です。</p>
</blockquote>

<a name="customizing.the.theme" id="customizing.the.theme"></a><h3>テーマをカスタマイズする</h3>

<p>カスタム要件を満たすために、モジュールの<code>templates/</code>フォルダーのなかでオーバーライド可能でフレームワークから継承された別の部分テンプレートが存在します。</p>

<p>ジェネレーターのテンプレートは個別に上書きできる小さな部分に分割可能で、アクションは一つずつ変更できます。</p>

<p>しかしながら、同じ方法でいくつかのモジュールに対してこれらを上書きしたいのであれば、おそらくは再利用可能なテーマを作るべきです。テーマ(theme)はテンプレートとアクションのサブセットで、<code>generator.yml</code>の始めでテーマの値に指定された場合、administrationモジュールのなかで使えます。デフォルトのテーマと一緒に、symfonyは<code>$sf_symfony_lib_dir/plugins/sfPropelPlugin/data/generator/sfPropelModule/admin/</code>で定義されたファイルを使います。</p>

<p>テーマのファイルは<code>data/generator/sfPropelModule/[theme_name]/</code>ディレクトリ内部の、プロジェクトのツリー構造に設置しなければならず、デフォルトのテーマからオーバーライドしたいファイルをコピーすることで新しいテーマを使い始めることができます(<code>$sf_symfony_lib_dir/plugins/sfPropelPlugin/data/generator/sfPropelModule/admin/</code>ディレクトリに設置されます):</p>

<pre><code>// 部分テンプレート、[theme_name]/template/templates/
_assets.php
_filters.php
_filters_field.php
_flashes.php
_form.php
_form_actions.php
_form_field.php
_form_fieldset.php
_form_footer.php
_form_header.php
_list.php
_list_actions.php
_list_batch_actions.php
_list_field_boolean.php
_list_footer.php
_list_header.php
_list_td_actions.php
_list_td_batch_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
_pagination.php
editSuccess.php
indexSuccess.php
newSuccess.php

// アクション、[theme_name]/parts
actionsConfiguration.php
batchAction.php
configuration.php
createAction.php
deleteAction.php
editAction.php
fieldsConfiguration.php
filterAction.php
filtersAction.php
filtersConfiguration.php
indexAction.php
newAction.php
paginationAction.php
paginationConfiguration.php
processFormAction.php
sortingAction.php
sortingConfiguration.php
updateAction.php
</code></pre>

<p>テンプレートファイルは実際にはテンプレートのテンプレート(templates of templates)であることに注意してください。すなわち、PHPファイルはジェネレーターの設定に基づくテンプレートを生成する特別なユーティリティによって解析されます(これはコンパイレーションフェーズ(compilation phase)と呼ばれます)。生成テンプレートが実際にブラウジングしている間に実行されるPHPコードを含まなければならないので、テンプレートのテンプレートは最初のパスの間にPHPコードを実行しないようにするために代替構文を使います。リスト14-40はデフォルトのテンプレートのテンプレートの抜粋です。</p>

<p>リスト14-40 - テンプレートのテンプレートの構文</p>

<pre class="yml">&lt;h1&gt;[?php echo &lt;?php echo $this-&gt;getI18NString('edit.title') ?&gt; ?]&lt;/h1&gt;
&nbsp;
[?php include_partial('&lt;?php echo $this-&gt;getModuleName() ?&gt;/flashes') ?]</pre>

<p>このリストにおいて、(コンパイル時に)<code>&lt;?</code>によって導入されたPHPコードは即座に実行され、<code>[?</code>によって導入されたものは実行時のみに実行されますが、テンプレートエンジンは最後には<code>[?</code>タグを<code>&lt;?</code>タグに変換するのでテンプレートの結果はつぎのようになります:</p>

<pre class="yml">&lt;h1&gt;&lt;?php echo __('List of all Articles') ?&gt;&lt;/h1&gt;
&nbsp;
&lt;?php include_partial('article/flashes') ?&gt;</pre>

<p>テンプレートのテンプレートは扱いにくいので、独自のテーマを作りたい場合、もっともお勧めすることは<code>admin</code>テーマから始め、少しずつ修正し、こまめにテストすることです。</p>

<blockquote class="tip"><p>
  ジェネレーターのテーマをプラグインのパッケージにすることができるので、再利用性が高くなり、複数のアプリケーションにまたがってデプロイすることが簡単になります。詳細な情報は17章を参照してください。</p>
</blockquote>

<p>-</p>

<blockquote class="sidebar"><p class="title">
  独自のジェネレーターを開発する</p>
  
  <p>administrationジェネレーターはsymfonyの内部コンポーネントのセットを使います。内部コンポーネントはキャッシュ内部に生成されたアクションとテンプレート、テーマの使用、テンプレートのテンプレートの解析を自動化します。</p>
  
  <p>このことはsymfonyが既存のジェネレーターとは似ているもしくは完全に異なる独自のジェネレーターを作るためのすべてのツールを提供することを意味します。モジュールの生成は<code>sfGeneratorManager</code>クラスの<code>generate()</code>メソッドで管理されます。たとえば、administrationを生成するために、symfonyは内部でつぎのコードを呼び出します:</p>

<pre class="yml">$manager = new sfGeneratorManager();
$data = $manager-&gt;generate('sfPropelGenerator', $parameters);</pre>
  
  <p>独自のジェネレーターを開発したい場合、<code>sfGeneratorManeger</code>クラスと<code>sfGenerator</code>クラスのAPIドキュメントを見て、<code>sfModelGenerator</code>クラスと<code>sfPropelGenerator</code>クラスを例としてください。</p>
</blockquote>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>バックエンドのアプリケーションを自動的に生成したい場合、基本はよく定義されたスキーマとオブジェクトモデルです。administrationのカスタマイズによって生成されたモジュールのカスタマイズの大半は設定を通して行われます。</p>

<p><code>generator.yml</code>ファイルは生成されたバックエンドのプログラミングにおいて中心的な役割を果たします。このファイルによって内容、機能、<code>list</code>、<code>new</code>と<code>edit</code>ビューの見た目を完全にカスタマイズできます。またPHPコードを一行も書かずに、フィールドラベル、ツールチップ、フィルター、ソートの順番、ページのサイズ、入力タイプ、外部のリレーション、カスタムインタラクション、とクレデンシャルをYAMLで直接管理できます。</p>

<p>administrationジェネレーターが必要な機能をネイティブにサポートしない場合、アクションをオーバーライドする部分テンプレートフィールドと機能は完全な拡張性を提供します。それに加えて、テーマのメカニズムのおかげで、administrationジェネレーターのメカニズムへの適合方法を再利用できます。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="13-I18n-and-L10n.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="15-Unit-and-Functional-Testing.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
